﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GrayParrot.Core
{
    public interface IReturnValueDefault
    {
        IReturnValueSuccessOrError OnDefault(Action action);
    }

    public interface IReturnValueError
    {
        IReturnValueSuccessOrDefault OnError(Action action);
    }

    public interface IReturnValueSuccess
    {
        IReturnValueErrorOrDefault OnSuccess(Action action);
    }

    public interface IReturnValueSuccessOrDefault: IReturnValueSuccess, IReturnValueDefault
    {
    }

    public interface IReturnValueSuccessOrError : IReturnValueSuccess, IReturnValueError
    {
    }

    public interface IReturnValueErrorOrDefault : IReturnValueError, IReturnValueDefault
    {
    }

    public class ReturnValue<T> : //where T : class, new()
                                  IReturnValueDefault, IReturnValueError, IReturnValueSuccess,
                                  IReturnValueSuccessOrDefault, IReturnValueSuccessOrError, IReturnValueErrorOrDefault
    {
        public bool HasError { get; set; }
        public bool HasValue { get; set; }
        public Exception Error { get; set; }
        public T Value { get; set; }
        public string Message { get; set; }

        public ReturnValue()
        {
            this.HasError = false;
            this.HasValue = false;
            this.Value = default(T);
            this.Error = null;
            this.Message = string.Empty;
        }

        public ReturnValue(T defaultValue)
        {
            this.HasError = false;
            this.HasValue = true;
            this.Value = defaultValue;
            this.Error = null;
            this.Message = string.Empty;
        }

        /// <summary>
        /// Withes the no value.
        /// </summary>
        /// <returns></returns>
        public static ReturnValue<T> WithNoValue()
        {
            ReturnValue<T> retValue = new ReturnValue<T>();
            retValue.HasValue = false;
            retValue.Value = default(T);

            return retValue;
        }

        /// <summary>
        /// Withes the default.
        /// </summary>
        /// <param name="hasValue">if set to <c>true</c> [has value].</param>
        /// <remarks>
        /// </remarks>
        /// <returns></returns>
        public static ReturnValue<T> WithDefault(bool hasValue)
        {
            ReturnValue<T> retValue = new ReturnValue<T>();
            retValue.HasValue = hasValue;
            retValue.Value = default(T);

            return retValue;
        }

        //public void SetError(string errorMessage)
        //{
        //    this.Error = new Exception(errorMessage);
        //    this.HasError = true;
        //    this.HasValue = false;
        //}

        //public void SetValue<T>(T value)
        //{
        //    this.HasError = false;
        //    this.HasValue = true;
        //    this.Value = value as T;
        //}

        //public void Initialize()
        //{
        //    this.Value = new T();
        //}

        public IReturnValueSuccessOrError OnDefault(Action action)
        {
            IReturnValueSuccessOrError p = (IReturnValueSuccessOrError)this;

            if (this.HasValue && 
                EqualityComparer<T>.Default.Equals(this.Value, default(T)))
            {
                action();
            }
            
            return p;
        }

        public IReturnValueSuccessOrDefault OnError(Action action)
        {
            if (this.HasError)
            {
                action();
            }

            return (IReturnValueSuccessOrDefault)this;
        }

        public IReturnValueErrorOrDefault OnSuccess(Action action)
        {
            IReturnValueErrorOrDefault p = (IReturnValueErrorOrDefault)this;

            if (this.HasValue &&
                !EqualityComparer<T>.Default.Equals(this.Value, default(T)))
            {
                action();
            }

            return p;
        }
    }
}
